%{
open Printf
open Ast
%}

%token <string> ID
%token <string> STRING
%token <string> REGEXP
%token <string> NUMBER


%token <string> UNARY1
%token <string> BINARY1
%token <string> UNARY2
%token <string> BINARY2
%token <string> UNARY3
%token <string> BINARY3
%token <string> UNARY4
%token <string> BINARY4
%token <string> UNARY5
%token <string> BINARY5
%token <string> UNARY6
%token <string> BINARY6
%token <string> UNARY7
%token <string> BINARY7
%token <string> UNARY8
%token <string> BINARY8
%token <string> UNARY9
%token <string> BINARY9
%token <string> UNARY10
%token <string> BINARY10
%token <string> ASSIGN_OP

%token COMMA

%token ASTERISK SLASH
%token PLUS MINUS


%left COMMA
%left ASSIGN_OP

%token EOF
%token VAR FUNCTION RETURN VOID IF ELSE SWITCH CASE DEFAULT NEW THROW TRY CATCH FOR WHILE BREAK CONTINUE DO IN GOTO
%token SELECT FROM WHERE LEFT RIGHT JOIN ON USING AS OUTER INNER CROSS NATURAL
%token LP RP LBRACKET RBRACKET LBRACE RBRACE
%token SEMI COLON

%start prog
%type <Ast.program> prog


%% 

prog:
  | stmt_list_opt EOF				{ Program $1 }

/*  
case_switch:
  | CASE expr2 COLON stmt_list_opt	{ Case ($2,$4) }
  | DEFAULT COLON stmt_list_opt 	{ Default($3) }

switch_list:
  | case_switch					{ $1::[] }
  | case_switch switch_list		{ $1::$2 }
*/

stmt:
  | stmt0			{ $1 }
  | stmt1			{ $1 }
  | BREAK id_opt 	{ Break $2 }
  | CONTINUE id_opt { Continue $2 }
  | ID COLON stmt1 	{ Label($1, $3) }

/*
if_stmt:
  | IF LP expr2 RP ifelse_chain 	{ If($3, $5) }

ifelse_chain:
  | stmt 				{ $1::[] }
  | stmt0 ELSE if_stmt 	{ ($1, Some $3) }
*/


stmt1:
/*
  | if_stmt											{ $1 }
  | TRY stmt CATCH LP formal_list_opt RP stmt		{ TryCatch($2, $5, $7) }
  | THROW expr1 									{ Throw($2) }
  | SWITCH LP expr1 RP LBRACE switch_list RBRACE	{ Switch($3, $6) }
  | FOR LP expr1 SEMI expr1 SEMI expr1 RP stmt 		{ For($3, $5, $7, $9) }
  | FOR LP var_expr SEMI expr1 SEMI expr1 RP stmt	{ ForVar($3, $5, $7, $9) }
  | FOR LP ID IN expr1 RP stmt 						{ ForIn($3, $5, $7) }
  | WHILE LP expr1 RP stmt							{ While($3, $5) }
  | DO stmt WHILE LP expr1 RP						{ Do($2, $5) }
*/
  | RETURN 											{ Return(Undefined) }
  | RETURN expr1									{ Return($2) }
  | VAR expr { Var($2) }
  
stmt0:
  | expr					{ Expr($1) }
  | LBRACE stmt_list RBRACE { Block($2) }


stmt_list:
  | stmt				{ $1::[] }
  | stmt SEMI stmt_list { $1::$3 }

stmt_list_opt:
  | 			{ [] }
  | stmt_list	{ $1 }


expr:
  | expr1 { $1 }
  | expr2 { $1 }

expr1:
  | expr2 COMMA expr							{ List($1, $3) }
  | expr2 binary_op expr 						{ BinaryOp($2, $1, $3) }
  | unary_op expr2								{ UnaryOp($1, $2) }
  | FUNCTION LP formal_list_opt RP expr  		{ Lambda($3, Expr $5) }

expr2:
  | atom										{ $1 }
  | ID											{ Id $1 }
  | LP expr RP									{ $2 }
  | LP select RP								{ $2 }
  | expr2 LP expr_opt_comma_list_opt RP			{ Call($1, $3) }
  | LBRACE prop_def_list_opt RBRACE	 			{ ObjectVal($2) }
  | LBRACKET expr_opt_comma_list_opt RBRACKET 	{ ArrayVal($2) }
  | expr3 										{ $1 }

expr3:
  | FUNCTION LP formal_list_opt RP LBRACE stmt RBRACE  	{ Lambda($3, $6)  }

expr_opt_comma_list_opt:
  | 									{ [] }
  | expr 								{ $1::[] }
  | COMMA 								{ Undefined::[] }
  | expr COMMA expr_opt_comma_list_opt 	{ $1::$3 }

prop_def_list_opt:
  | 									{ [] }
  | prop_def							{ $1::[] }
  | prop_def COMMA prop_def_list_opt	{ $1::$3 }

prop_def:
  | ID COLON expr	{ ($1, $3) }
  
atom:
  | NUMBER 				{ Number($1) }
  | STRING 				{ String($1) }
  | REGEXP 				{ Regexp($1) }

formal_list_opt:
  | 							{ [] }
  | ID							{ $1::[] }
  | ID COMMA formal_list_opt	{ $1::$3 }

id_opt:
  | 	{ None }
  | ID	{ Some $1 }

select:
  | SELECT select_expr_comma_list { Select($2, [], True) }
  | SELECT select_expr_comma_list FROM table_ref_list { Select($2, $4, True) }
  | SELECT select_expr_comma_list FROM table_ref_list WHERE expr { Select($2, $4, $6) }


table_factor:
  | table_id 			{ Table($1,$1) }
  | table_id as_opt ID	{ Table($3,$1) }

table_ref_list:
  | table_ref						{ $1::[] }
  | table_ref COMMA table_ref_list 	{ $1::$3 }

table_ref:
  | table_factor 											{ $1 }
  | table_ref LEFT outer_opt JOIN table_ref join_condition 	{ LeftJoin($1, $5, $6) }
  | table_ref RIGHT outer_opt JOIN table_ref join_condition { RightJoin($1, $5, $6) }
  | table_ref INNER JOIN table_factor join_condition		{ InnerJoin($1, $4, $5) }
  | table_ref CROSS JOIN table_factor 						{ CrossJoin($1, $4) }
  | table_ref NATURAL LEFT JOIN table_factor  				{ NaturalLeftJoin($1, $5) }
  | table_ref NATURAL RIGHT JOIN table_factor  				{ NaturalRightJoin($1, $5) }

join_condition:
  | ON sql_expr					{ On($2) }
  | USING LP column_list RP		{ Using($3) }

column_list:
  | ID						{ $1::[] }
  | ID COMMA column_list	{ $1::$3 }

select_expr_comma_list:
  | select_expr									{ $1::[] }
  | select_expr COMMA select_expr_comma_list	{ $1::$3 }

select_expr:
  | sql_expr 			{ ("", $1) }
  | sql_expr as_opt ID	{ ($3, $1) }

sql_expr:
  | expr				{ $1 }


binary_op:
  | BINARY1		{ $1 }
  | BINARY2		{ $1 }
  | BINARY3		{ $1 }
  | BINARY4		{ $1 }
  | BINARY5		{ $1 }
  | BINARY6		{ $1 }
  | BINARY7		{ $1 }
  | BINARY8		{ $1 }
  | BINARY9		{ $1 }
  | BINARY10	{ $1 }
  | ASSIGN_OP	{ $1 }

unary_op:
  | UNARY1		{ $1 }
  | UNARY2		{ $1 }
  | UNARY3		{ $1 }
  | UNARY4		{ $1 }
  | UNARY5		{ $1 }
  | UNARY6		{ $1 }
  | UNARY7		{ $1 }
  | UNARY8		{ $1 }
  | UNARY9		{ $1 }
  | UNARY10		{ $1 }
  


 
table_id:
  | ID		{ $1 }

outer_opt:
  |   		{}
  | OUTER	{}

as_opt:
  | 	{}
  | AS 	{}





